/***************************************************************************
 *   Copyright (C) 2007 - 2009 by Andreas Theofilu                         *
 *   andreas@theosys.at                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation version 3 of the License.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/*
 * Ported to KDE4 on Jul. 8 2009 by Andreas Theofilu.
 */

#include "progresswidget.h"
#include <kapplication.h>
#include <kmessagebox.h>
#include <KConfig>
#include <klocale.h>
#include <qlabel.h>
#include <QProgressBar>
#include "garmin.h"
#include "../libgant/gant.h"

#include <iostream>

using std::cout;
using std::endl;

extern KApplication *mapp;
progressWidget *me;

progressWidget::progressWidget ( QWidget* parent, Qt::WFlags fl )
		: QDialog(parent, fl), Ui::Progress ()
{
	setupUi (this);
	step = 0;
	psteps = 7;
	me = this;
}

progressWidget::~progressWidget()
{
	me = 0;
}

/*
 * This function is called from the Garmin library libgarmin.a during
 * reading data from a GPS device.
 */
void progressWidget::CallBack(char *)
{
	if (me)
	   me->IncrementBar();
}

void progressWidget::IncrementBar()
{
	step++;

	if (psteps <= step)
	{
	   psteps += 2;
	   barProgress->setRange(0, psteps);
	}

	barProgress->setValue(step);
}

/*$SPECIALIZATION$*/
/*
 * The following function reads out all data from a GPS device. It uses
 * the library libgarmin.a, who is basicly the "garmintools-0.5", written by
 * Dave Bailey (thanks Dave!).
 */
bool progressWidget::Download()
{
QString Data, filepath, device;
garmin_unit         garmin;
garmin_data *       data;
garmin_data *       data0;
garmin_data *       data1;
garmin_data *       data2;
garmin_data *       rlaps;
garmin_data *       rtracks;
garmin_list *       runs   = NULL;
garmin_list *       laps   = NULL;
garmin_list *       tracks = NULL;
garmin_data *       rlist;
garmin_list_node *  n;
garmin_list_node *  m;
uint32              trk;
uint32              f_lap;
uint32              l_lap;
uint32              l_idx;
time_type           start;
time_t              start_time;
char                filename[BUFSIZ];
bool		    Serial, Forerunner;
struct tm *         tbuf;

	// Find the data path in config file
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
	KConfigGroup ic (&cfg, "SportWatcher");
	Data = ic.readEntry("Data", QString("~/.sportwatcher"));
	device = ic.readEntry("Device", QString("/dev/ttyUSB1"));
	Serial = ic.readEntry("Serial", false);
	Forerunner = ic.readEntry("Forerunner", false);

	if (Data.isEmpty())
	{
	   KMessageBox::error(this, i18n("No data path was set in the settings!"));
	   return false;
	}

	if (!isVisible())
	   setShown(true);

	barProgress->setRange(0, psteps);
	step = 0;
	garmin_set_device(device.toAscii());
	garmin_set_method((Serial) ? 1 : 0);		// Setting to 1 doesn't work!! (reading over garmin_gps kernel module)
	// Initialize a callback function to have a real progress bar
	garmin_set_hook(&progressWidget::CallBack);

	/*
	 * In case we should read the data out of a Forerunner 50 or
	 * Forerunner 405 or similar, we must do that with the help of
	 * the ANT library. It inserts everything into the garmin structures,
	 * so we can use the same procedure for every device.
	 */
	if (Serial && Forerunner)	// Forerunner 50?
	{
	gant *ga;
	uchar *buffer;
	int size;
	QString auth = Data + QString("/.auth50");

	   ga = new gant(auth);		// Authority file
	   ga->setDevice(device);	// Special device (serial port)
	   ga->read_device ();		// FIXME: Parameter to tell the type of Forerunner

	   if ((buffer = ga->getBuffer (&size)) == 0)
	   {
	      delete ga;
	      return false;
	   }

	   // FIXME: Here we should decode the data buffer
	   delete ga;
	}
/*	else if (Serial && !Forerunner)	// Forerunner 405
	{
	gant *ga;
	QString auth = Data + QString("/.auth405");

	   ga = new gant(auth);		// Authority file
	   ga->setDevice(device);	// Special device (serial port)
	   ga->read_device ();		// FIXME: Parameter to tell the type of Forerunner
	   // FIXME: Here we need a function to get the data buffer
	   delete ga;
	} */
	else	// We read from a Forerunner 305 or compatible
	{
	   lblInfo->setText(i18n("Looking for a Garmin GPS device..."));

	   if (garmin_init(&garmin, 0) == 0)
	   {
	      garmin_close(&garmin);
	      garmin_clear_hook();
	      return false;
	   }

	   barProgress->setValue(++step);
	   lblInfo->setText(i18n("Extracting data from Garmin <i>") + QString(garmin.product.product_description) + QString("</i>"));
	   lblReading->setText(i18n("Please wait, this may take some time!"));

	   mapp->processEvents();

	   if ((data = garmin_get(&garmin, GET_RUNS)) != NULL )
	   {
	      /*
	       * We should have a list with three elements:
	       *
	       * 1) The runs (which identify the track and lap indices)
	       * 2) The laps (which are related to the runs)
	       * 3) The tracks (which are related to the runs)
	       */

	      barProgress->setValue(++step);
	      lblReading->setText(i18n("Sorting out runs ..."));
	      mapp->processEvents();
	      data0 = garmin_list_data(data, 0);
	      barProgress->setValue(++step);
	      lblReading->setText(i18n("Sorting out laps ..."));
	      mapp->processEvents();
	      data1 = garmin_list_data(data, 1);
	      barProgress->setValue(++step);
	      lblReading->setText(i18n("Sorting out tracks ..."));
	      mapp->processEvents();
	      data2 = garmin_list_data(data, 2);
	      barProgress->setValue(++step);
	      lblReading->setText(i18n("Sorting out data blocks done."));
	      mapp->processEvents();

	      if ( data0 != NULL && (runs   = (garmin_list *)data0->data) != NULL &&
		data1 != NULL && (laps   = (garmin_list *)data1->data) != NULL &&
		data2 != NULL && (tracks = (garmin_list *)data2->data) != NULL )
	      {

		 /* For each run, get its laps and track points. */
		 /* But first see how much runs to set the progress bar */
		 for (n = runs->head; n != NULL; n = n->next)
		    psteps++;
	
		 barProgress->setRange(0, psteps);
		 mapp->processEvents();

		 for (n = runs->head; n != NULL; n = n->next)
		 {
		    barProgress->setValue(++step);
		    mapp->processEvents();

		    if (get_run_track_lap_info(n->data, &trk, &f_lap, &l_lap) != 0)
		    {
		       lblReading->setText(i18n("Running: track ") + QString("%1, ").arg(trk) + i18n("laps ") + QString("%1:").arg(f_lap) + QString("%1").arg(l_lap));
		       mapp->processEvents();
		       start = 0;

		       /* Get the laps. */
		       rlaps = garmin_alloc_data(data_Dlist);

		       for (m = laps->head; m != NULL; m = m->next)
		       {
			  if (get_lap_index(m->data, &l_idx) != 0)
			  {
			     if ( l_idx >= f_lap && l_idx <= l_lap )
			     {
			        garmin_list_append((garmin_list *)rlaps->data, m->data);

			        if ( l_idx == f_lap )
				   get_lap_start_time(m->data, &start);
			     }
			  }
		       }

		       /* Get the track points. */

		       rtracks = get_track(tracks,trk);

		       /* Now make a three-element list for this run. */
		       rlist = garmin_alloc_data(data_Dlist);
		       garmin_list_append((garmin_list *)rlist->data,n->data);
		       garmin_list_append((garmin_list *)rlist->data,rlaps);
		       garmin_list_append((garmin_list *)rlist->data,rtracks);

		       /*
		        * Determine the filename based on the start time of the first lap
		        */

		       if ((start_time = start) != 0)
		       {
			  QByteArray ba = Data.toAscii();

			  tbuf = localtime(&start_time);
			  filepath.sprintf("%s/%d/%02d",
			     ba.data(), tbuf->tm_year+1900, tbuf->tm_mon + 1);
			  strftime(filename, sizeof(filename), "%Y%m%dT%H%M%S.gmn", tbuf);

			  /* Save rlist to the file. */
			  garmin_save(rlist, filename, filepath.toAscii());
			  lblReading->setText(i18n("Saved file ") + QString(filename) + i18n(" successfully."));
			  mapp->processEvents();
		       }
	               else
			  KMessageBox::error(this, i18n("Start time of first lap not found!"));

		       /* Free the temporary lists we were using. */

		       if (rlaps != NULL)
		       {
			  garmin_free_list_only((garmin_list *)rlaps->data);
			  free(rlaps);
		       }

		       if (rtracks != NULL)
		       {
			  garmin_free_list_only((garmin_list *)rtracks->data);
			  free(rtracks);
		       }

		       if (rlist != NULL)
		       {
			  garmin_free_list_only((garmin_list *)rlist->data);
			  free(rlist);
		       }
		    }
		 }
	      }
	      else
	      {
		 if (data0 == NULL)
		    KMessageBox::error(this, i18n("Toplevel data missing element 0 (runs)"));
		 else if (runs == NULL)
		    KMessageBox::error(this, i18n("No runs extracted!"));

		 if ( data1 == NULL )
		    KMessageBox::error(this, i18n("Toplevel data missing element 1 (laps)"));
		 else if (laps == NULL)
		    KMessageBox::error(this, i18n("No laps extracted!"));

		 if (data2 == NULL)
		    KMessageBox::error(this, i18n("Toplevel data missing element 2 (tracks)"));
		 else if (tracks == NULL)
		    KMessageBox::error(this, i18n("No tracks extracted!"));
	      }

	      garmin_free_data(data);
	   }
	   else
	      KMessageBox::error(this, i18n("Unable to extract any data!"));
	}

	mapp->processEvents();
	garmin_close(&garmin);
	garmin_clear_hook();

	if (garmin_count_error() > 0)
	   return false;

	return true;
}

int progressWidget::get_run_track_lap_info ( garmin_data * run,
			 uint32 *      track_index,
			 uint32 *      first_lap_index,
			 uint32 *      last_lap_index )
{
  D1000 * d1000;
  D1009 * d1009;
  D1010 * d1010;

  int ok = 1;

  switch ( run->type ) {
  case data_D1000:
    d1000            = (D1000 *)run->data;
    *track_index     = d1000->track_index;
    *first_lap_index = d1000->first_lap_index;
    *last_lap_index  = d1000->last_lap_index;
    break;
  case data_D1009:
    d1009            = (D1009 *)run->data;
    *track_index     = d1009->track_index;
    *first_lap_index = d1009->first_lap_index;
    *last_lap_index  = d1009->last_lap_index;
    break;
  case data_D1010:
    d1010            = (D1010 *)run->data;
    *track_index     = d1010->track_index;
    *first_lap_index = d1010->first_lap_index;
    *last_lap_index  = d1010->last_lap_index;
    break;
  default:
    fprintf(stderr, "get_run_track_lap_info: run type %d invalid!\n",run->type);
    ok = 0;
    break;
  }

  return ok;
}

int progressWidget::get_lap_index ( garmin_data * lap, uint32 * lap_index )
{
  D1001 * d1001;
  D1011 * d1011;
  D1015 * d1015;

  int ok = 1;

  switch ( lap->type ) {
  case data_D1001:
    d1001      = (D1001 *)lap->data;
    *lap_index = d1001->index;
    break;
  case data_D1011:
    d1011      = (D1011 *)lap->data;
    *lap_index = d1011->index;
    break;
  case data_D1015:
    d1015      = (D1015 *)lap->data;
    *lap_index = d1015->index;
    break;
  default:
    fprintf(stderr, "get_lap_index: lap type %d invalid!\n",lap->type);
    ok = 0;
    break;
  }

  return ok;
}


int progressWidget::get_lap_start_time (garmin_data *lap, time_type *start_time)
{
  D1001 * d1001;
  D1011 * d1011;
  D1015 * d1015;

  int ok = 1;

  switch ( lap->type ) {
  case data_D1001:
    d1001       = (D1001 *)lap->data;
    *start_time = d1001->start_time + TIME_OFFSET;
    break;
  case data_D1011:
    d1011       = (D1011 *)lap->data;
    *start_time = d1011->start_time + TIME_OFFSET;
    break;
  case data_D1015:
    d1015       = (D1015 *)lap->data;
    *start_time = d1015->start_time + TIME_OFFSET;
    break;
  default:
    fprintf(stderr, "get_lap_start_time: lap type %d invalid!\n",lap->type);
    ok = 0;
    break;
  }

  return ok;
}


garmin_data *progressWidget::get_track(garmin_list *points, uint32 trk_index)
{
  garmin_list_node * n;
  garmin_data *      track = NULL;
  D311 *             d311;
  int                done = 0;

  /* Look for a data_D311 with an index that matches. */

  for ( n = points->head; n != NULL; n = n->next ) {    
    if ( n->data != NULL ) {
      switch ( n->data->type ) {
      case data_D311:
	if ( track == NULL ) {
	  d311 = (D311 *)n->data->data;
	  if ( d311->index == trk_index ) {
	    track = garmin_alloc_data(data_Dlist);
	    garmin_list_append((garmin_list *)track->data,n->data);
	  }
	} else {
	  /* We've reached the end of the track */
	  done = 1;
	}
	break;
      case data_D300:
      case data_D301:
      case data_D302:
      case data_D303:
      case data_D304:
	if ( track != NULL ) {
	  garmin_list_append((garmin_list *)track->data,n->data);
	}
	break;
      default:
	fprintf(stderr, "get_track: point type %d invalid!\n",n->data->type);
	break;
      }
    }

    if ( done != 0 ) break;
  }

  return track;
}


#include "progresswidget.moc"

